/*
 * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears
 * the following copyright notice:
 *
 * Copyright (c) 1983, 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the University nor the names of its contributors
 *	  may be used to endorse or promote products derived from this software
 *	  without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "c.h"

#include <sys/stat.h>


/*
 * pg_mkdir_p --- 创建一个目录，如果必要，还要创建父目录
 *
 * 这相当于 "mkdir -p"，只是如果目标目录已经存在，我们不会抱怨。
 *
 * 我们假设路径是规范形式，即使用 / 作为分隔符。
 *
 * omode 是目标目录的文件权限位。注意，任何需要创建的父目录的权限将根据当前的 umask 设置，但强制加上 u+wx，以确保我们可以在这里创建。
 * （我们将 omode 声明为 int，而不是 mode_t，以最小化对 port.h 的依赖。）
 *
 * 成功返回 0，失败返回 -1（并设置 errno）。
 *
 * 注意，在失败时，path 参数已被修改以表示我们遇到问题的特定目录级别。
 */
int pg_mkdir_p(char *fc_path, int fc_omode)
{
	struct stat fc_sb;
	mode_t		fc_numask,
				fc_oumask;
	int			fc_last,
				fc_retval;
	char	   *fc_p;

	fc_retval = 0;
	fc_p = fc_path;

#ifdef WIN32
	/* 跳过 win32 的网络和驱动器规范 */
	if (strlen(p) >= 2)
	{
		if (p[0] == '/' && p[1] == '/')
		{
			/* 网络驱动器 */
			p = strstr(p + 2, "/");
			if (p == NULL)
			{
				errno = EINVAL;
				return -1;
			}
		}
		else if (p[1] == ':' &&
				 ((p[0] >= 'a' && p[0] <= 'z') ||
				  (p[0] >= 'A' && p[0] <= 'Z')))
		{
			/* 本地驱动器 */
			p += 2;
		}
	}
#endif

	/*
	 * POSIX 1003.2：对于每个未命名现有目录的 dir 操作数，将发生等效于以下命令的效果：
	 *
	 * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir
	 *
	 * 我们更改用户的 umask，然后恢复它，而不是执行 chmod。注意，我们假设 umask() 不会改变 errno。
	 */
	fc_oumask = umask(0);
	fc_numask = fc_oumask & ~(S_IWUSR | S_IXUSR);
	(void) umask(fc_numask);

	if (fc_p[0] == '/')			/* 跳过前导 '/'。 */
		++fc_p;
	for (fc_last = 0; !fc_last; ++fc_p)
	{
		if (fc_p[0] == '\0')
			fc_last = 1;
		else if (fc_p[0] != '/')
			continue;
		*fc_p = '\0';
		if (!fc_last && fc_p[1] == '\0')
			fc_last = 1;

		if (fc_last)
			(void) umask(fc_oumask);

		/* 检查是否存在预先存在的目录 */
		if (stat(fc_path, &fc_sb) == 0)
		{
			if (!S_ISDIR(fc_sb.st_mode))
			{
				if (fc_last)
					errno = EEXIST;
				else
					errno = ENOTDIR;
				fc_retval = -1;
				break;
			}
		}
		else if (mkdir(fc_path, fc_last ? fc_omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
		{
			fc_retval = -1;
			break;
		}
		if (!fc_last)
			*fc_p = '/';
	}

	/* 确保我们恢复了 umask */
	(void) umask(fc_oumask);

	return fc_retval;
}
